package game.block.screen;

import game.block.Constants;
import game.block.Locator;
import game.block.game.Board;
import game.block.game.GameState;
import game.block.input.InputHandler;
import game.block.tutorial.Tutorial;

import java.io.IOException;
import java.util.ArrayList;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureWrap;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;

public class BoardScreen implements Screen, Constants {

	private OrthographicCamera camera;
	private BitmapFont font, fontSmall, fontMedium;
	private SpriteBatch gameSprites;
	private SpriteBatch uiBatch;
	private Board gameBoard;
	private Vector3 bounds;
	private GameState gs;
	private Tutorial tutorial;
	private ImageButton backButton, undoButton, resetButton, forwardButton, backwardButton;
	private Stage stage;
	private Table table, table2;
	private final ArrayList<TransitionEffect> effects;
	private Texture background;
	public static float c = 0.31F;
	public static int COUNT = 3;
	private Sprite borderSprite;
	private Vector3 negBounds;

	public BoardScreen(Board board, OrthographicCamera camera, GameState gs) {
		gameBoard = board;
		uiBatch = new SpriteBatch();
		this.camera = camera;
		this.gs = gs;
		borderSprite = new Sprite(new Texture(BORDER));
		background = new Texture(BACKGROUND);
		background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
		tutorial = new Tutorial();
		effects = new ArrayList<TransitionEffect>();
		font = new BitmapFont(Gdx.files.internal("data/white48.fnt"), false);
		fontSmall = new BitmapFont(Gdx.files.internal("data/white.fnt"), false);
		fontMedium = new BitmapFont(Gdx.files.internal("data/white40.fnt"), false);
		gameSprites = new SpriteBatch();
		Locator.getInputMultiplexer().addProcessor(new InputHandler(gameBoard, camera));
		gameBoard.setScreen(this);
		try {
			gameBoard.initNewBoard(Locator.getLevel());
		} catch (IOException e) {
			e.printStackTrace();
		}

		createTutorialButtons();
		// create ui buttons
		createBackButton();
		createUndoButton();
		createResetButton();
		table = new Table();
		table2 = new Table();
		table.setBounds(Gdx.graphics.getWidth() * 0.905f, Gdx.graphics.getHeight() * 0.00f, Gdx.graphics.getWidth() * 0.12f,
				Gdx.graphics.getHeight() * 0.38f);
		stage = new Stage();
		table.add(backButton).expandY(); // top
		table.row();
		table.add(resetButton).expandY(); // middle
		table.row();
		table.add(undoButton).expandY().bottom(); // bottom
		// table.scale(SCALE);
		stage.addActor(table);
		updateBounds();

		table2.setBounds(0.0f, Gdx.graphics.getHeight() * 0.77f, Gdx.graphics.getWidth() * 0.43f, Gdx.graphics.getHeight() * 0.1f);
		updateTutorialTable();

		Locator.getInputMultiplexer().addProcessor(stage);
	}

	public void backToMenu() {
		final Screen current = this;
		effects.clear();
		effects.add(new FadeOutTransitionEffect(0.5f));
		effects.add(new FadeInTransitionEffect(0.5f));
		Screen transitionScreen = new TransitionScreen(Locator.getGame(), current, gs.getMainMenu(), effects);
		Locator.getGame().setScreen(transitionScreen);
	}

	@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		uiBatch.begin();
		uiBatch.draw(background, 0, 0, background.getWidth() * COUNT, background.getHeight() * COUNT, 0, COUNT, COUNT, 0);
		uiBatch.end();

		gameSprites.begin();
		gameSprites.setProjectionMatrix(camera.combined);
		gameBoard.render(gameSprites);
		gameSprites.end();
		uiBatch.begin();
		updateParticles(delta);
		renderGUI();
		uiBatch.end();

		update(delta);
		stage.act(delta);
		stage.draw();
		getInput();

	}

	public void updateParticles(float delta) {
		ArrayList<ParticleEffect> removeList = new ArrayList<ParticleEffect>();
		for (int i = 0; i < Locator.getEffects().size(); i++) {
			Locator.getEffects().get(i).draw(uiBatch, delta);
			if (Locator.getEffects().get(i).isComplete()) {
				removeList.add(Locator.getEffects().get(i));
			}
		}
		Locator.getEffects().removeAll(removeList);
	}

	private void renderGUI() {
		// render ui text
		uiBatch.draw(borderSprite.getTexture(), 0, 0, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 1, 1, 0, 0, 0,
				(int) borderSprite.getWidth(), (int) borderSprite.getHeight(), false, false);
		font.drawMultiLine(uiBatch, "Puzzle  " + Locator.getLevel(), Gdx.graphics.getWidth() * 0.445f, Gdx.graphics.getHeight() * 0.98f);
		font.drawMultiLine(uiBatch, "Moves:  " + gameBoard.getMoves(), Gdx.graphics.getWidth() * 0.86f, Gdx.graphics.getHeight() * 0.98f);
		// render tutorial info
		fontMedium.drawWrapped(uiBatch, gameBoard.getSelectedInfo(), Gdx.graphics.getHeight() * 0.01f, Gdx.graphics.getHeight() * 0.35f,
				Gdx.graphics.getWidth() * 0.21f);
		tutorial.render(fontSmall, uiBatch);
		
		// render tooltips
		if (undoButton.isOver()) {
			font.draw(uiBatch, "Undo", Gdx.graphics.getWidth() * 0.83f, Gdx.graphics.getHeight() * 0.08f);
		}
		// render tooltips
		if (resetButton.isOver()) {
			font.draw(uiBatch, "Reset", Gdx.graphics.getWidth() * 0.83f, Gdx.graphics.getHeight() * 0.21f);
		}
		// render tooltips
		if (backButton.isOver()) {
			font.draw(uiBatch, "Back", Gdx.graphics.getWidth() * 0.83f, Gdx.graphics.getHeight() * 0.35f);
		}
	}

	public void update(float delta) {
		camera.update();
		gameBoard.update(delta);
		tutorial.update(delta);
	}

	public void getInput() {
		// create bounds for camera movement
		Vector3 cameraPos = new Vector3(camera.position);
		if (Gdx.input.isKeyPressed(Keys.A) && cameraPos.x > negBounds.x) {
			camera.translate(-4, 0);
		}
		if (Gdx.input.isKeyPressed(Keys.D) && cameraPos.x < bounds.x) {
			camera.translate(4, 0);
		}
		if (Gdx.input.isKeyPressed(Keys.S) && cameraPos.y > negBounds.y) {
			camera.translate(0, -4);
		}
		if (Gdx.input.isKeyPressed(Keys.W) && cameraPos.y < bounds.y) {
			camera.translate(0, 4);
		}
		camera.position.x = Math.min(camera.position.x, bounds.x);
		camera.position.x = Math.max(camera.position.x, negBounds.x);
		camera.position.y = Math.min(camera.position.y, bounds.y);
		camera.position.y = Math.max(camera.position.y, negBounds.y);
	}
	
	public void moveCamera(float x, float y){
		if(camera.position.x - negBounds.x >= 0 && bounds.x - camera.position.x >= 0 ){
			camera.translate(x,0);
		}
		if(camera.position.y - negBounds.y >= 0 && bounds.y - camera.position.y >= 0 ){
			camera.translate(0,y);
		}
        camera.update();
	}

	@Override
	public void resize(int width, int height) {
		// ratio=(float)width/(float)height;
		// camera.setToOrtho(false, width * SCALE, height * SCALE);
		camera.update();
	}

	@Override
	public void show() {
		// reset fade effects
		effects.clear();
		effects.add(new FadeOutTransitionEffect(0.5f));
		effects.add(new FadeInTransitionEffect(0.5f));

		updateBounds();
		resume();
	}

	public void updateBounds() {
		// update bounds for camera movement each time new board is created
		float maxBoardWidth = ((gameBoard.getRows() + 3) * TILE_SIZE);
		float maxBoardHeight = ((gameBoard.getCols() + 3) * TILE_SIZE);
		float maxNegBoardWidth = ((gameBoard.getRows()) * TILE_SIZE);
		float maxNegBoardHeight = ((gameBoard.getCols()) * TILE_SIZE);

		// bounds for window panning
		bounds = new Vector3(Math.max(maxBoardWidth - camera.viewportWidth / 2, gameBoard.getRows() * (TILE_SIZE / 2)), Math.max(maxBoardHeight - camera.viewportHeight / 2,
				gameBoard.getCols() * (TILE_SIZE / 2)), 0);
		negBounds = new Vector3(Math.min(camera.viewportWidth / 2 - TILE_SIZE * 6, maxNegBoardWidth / 2), Math.min(camera.viewportHeight / 2 - TILE_SIZE * 3,
				maxNegBoardHeight / 2), 0);
	}

	public Tutorial getTutorial() {
		return tutorial;
	}

	@Override
	public void hide() {
		pause();
	}

	@Override
	public void pause() {
		Gdx.input.setInputProcessor(null);
		gameBoard.setPause(true);
	}

	@Override
	public void resume() {
		Gdx.input.setInputProcessor(Locator.getInputMultiplexer());
		gameBoard.setPause(false);

	}

	@Override
	public void dispose() {
		for (int i = 0; i < gameBoard.getEntities().size(); i++) {
			gameBoard.getEntities().get(i).dispose();
		}

	}

	private void createResetButton() { // middle
		Image image = new Image(new Texture(Gdx.files.internal("data/art/restartButton.png")));
		resetButton = new ImageButton(image.getDrawable());
		resetButton.addListener(new ClickListener() {
			@Override
			public void clicked(InputEvent event, float x, float y) {
				Locator.getAudio().playSound(SOUND.RESPAWN.getValue());
				try {
					gameBoard.restart();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}

	private void createUndoButton() { // bottom of buttons
		Image image = new Image(new Texture(Gdx.files.internal("data/art/undoButton.png")));
		undoButton = new ImageButton(image.getDrawable());
		undoButton.addListener(new ClickListener() {
			@Override
			public void clicked(InputEvent event, float x, float y) {
				Locator.getAudio().playSound(SOUND.UNDO.getValue());
				gameBoard.undo();
			}
		});
	}

	private void createBackButton() { // top
		final Screen current = this;
		effects.add(new FadeOutTransitionEffect(0.5f));
		effects.add(new FadeInTransitionEffect(0.5f));
		Image image = new Image(new Texture(Gdx.files.internal("data/art/backButton.png")));
		backButton = new ImageButton(image.getDrawable());
		backButton.addListener(new ClickListener() {
			@Override
			public void clicked(InputEvent event, float x, float y) {
				Locator.getAudio().playSound(SOUND.HOVER.getValue());
				Screen transitionScreen = new TransitionScreen(Locator.getGame(), current, gs.getMainMenu(), effects);
				Locator.getGame().setScreen(transitionScreen);
			}
		});
	}

	private void createTutorialButtons() {
		Image image = new Image(new Texture(Gdx.files.internal("data/art/backButton.png")));
		backwardButton = new ImageButton(image.getDrawable());
		image.setScale(0.5f);
		backwardButton.setScale(0.5f);
		backwardButton.addListener(new ClickListener() {
			@Override
			public void clicked(InputEvent event, float x, float y) {
				Locator.getAudio().playSound(SOUND.HOVER.getValue());
				tutorial.previous();
			}
		});

		image = new Image(new Texture(Gdx.files.internal("data/art/forwardButton.png")));
		forwardButton = new ImageButton(image.getDrawable());
		image.setScale(0.5f);
		forwardButton.setScale(0.5f);
		forwardButton.addListener(new ClickListener() {
			@Override
			public void clicked(InputEvent event, float x, float y) {
				Locator.getAudio().playSound(SOUND.HOVER.getValue());
				tutorial.next();
			}
		});
	}

	public void updateTutorialTable() {
		table2.clear();
		if (tutorial.hasTutorial() && tutorial.hasMultiplieTutorials()) {
			table2.add(backwardButton).left().expandX();
			table2.add(forwardButton).expandX();
			stage.addActor(table2);
		}
	}

	// start at level 1
	public void newGame() {
		try {
			gameBoard.initNewBoard(1);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void setLevel(int i) {
		try {
			gameBoard.initNewBoard(i);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
